import { jiaZaiAmmo, AmmoLib } from './AmmoLib.js'
import { WuLiWuTi } from './wuTi/WuLiWuTi.js'
import { WuLiGangTi } from './wuTi/WuLiGangTi.js'
import { WuLiJueSe } from './wuTi/WuLiJueSe.js'
import { WuLiChe } from './wuTi/WuLiChe.js'
import { WuLiRouTi } from './wuTi/WuLiRouTi.js'
import { WuLiYueShu } from './yueShu/WuLiYueShu.js'

class WuLiYinQing {
	collisionConfiguration
	dispatcher
	broadphase
	solver
	softBodySolver
	world

	intervalID = null
	yinCang = false // 页面是否隐藏
	tingZhi = false // 是否停止物理模拟
	zhenShu = 60 // 帧数
	shangShi = 0 // 上次时间

	constructor() {
		// 配置物理世界
		this.collisionConfiguration = new AmmoLib.btSoftBodyRigidBodyCollisionConfiguration()
		this.dispatcher = new AmmoLib.btCollisionDispatcher(this.collisionConfiguration)
		this.broadphase = new AmmoLib.btDbvtBroadphase()
		this.solver = new AmmoLib.btSequentialImpulseConstraintSolver()
		this.softBodySolver = new Ammo.btDefaultSoftBodySolver()
		this.world = new AmmoLib.btSoftRigidDynamicsWorld(this.dispatcher, this.broadphase, this.solver, this.collisionConfiguration, this.softBodySolver)
		
		// 柔体 在：stepSimulation固定步长1/120、位置y: 16384、重力 y: -9.8时重力会失效
		this.world.setGravity(new AmmoLib.btVector3(0, 0, 0))
		this.world.getWorldInfo().set_m_gravity( new Ammo.btVector3( 0, 0, 0 ) );
		
		// 独立于主线程的计时模拟
		// this.intervalID = setInterval( () => {
		// 	if (this.yinCang) return
		// 	this.buJinMoNi()
		// }, 1000 / this.zhenShu )
	}
	// 尺寸坐标等过大各种抖动等问题会增多
	// 添加物体
	tianJiaWuTi(canShu) {
		let wuLiWuTi = null

		if (canShu.wuTiLeiXing == '刚体') wuLiWuTi = new WuLiGangTi(canShu, this.world)
		
		if (canShu.wuTiLeiXing == '柔体') wuLiWuTi = new WuLiRouTi(canShu, this.world)
		
		if (canShu.wuTiLeiXing == '角色') wuLiWuTi = new WuLiJueSe(canShu, this.world)
		
		if (canShu.wuTiLeiXing == '车') wuLiWuTi = new WuLiChe(canShu, this.world)
		
		if (wuLiWuTi) WuLiWuTi.wuLiWuTiShuZu.push(wuLiWuTi)
	}
	// 移除物体
	yiChuWuTi(canShu) {
		WuLiWuTi.yiChuWuTi(canShu)
	}
	// 设置物体
	sheZhiWuTi(canShu) {
		WuLiWuTi.sheZhiWuTi(canShu)
	}
	// 编辑形状
	bianJiXingZhuang(canShu) {
		WuLiWuTi.bianJiXingZhuang(canShu)
	}
	// 添加约束
	tianJiaYueShu(canShu) {
		let wuLiYueShu = new WuLiYueShu(canShu, this.world)
		
		WuLiYueShu.wuLiYueShuShuZu.push(wuLiYueShu)
	}
	// 设置约束
	sheZhiYueShu(canShu) {
		WuLiYueShu.sheZhiYueShu(canShu)
	}
	// 移除约束
	yiChuYueShu(canShu) {
		WuLiYueShu.yiChuYueShu(canShu)
	}
	// 返回物体状态
	huoQuWuTi(jianGeShi) {
		this.buJinMoNi(jianGeShi)
		// 更新物体状态
		let jieGuoShuZu = []
		for (let i = 0, il = WuLiWuTi.wuLiWuTiShuZu.length; i < il; i++) {
			const wuLiWuTi = WuLiWuTi.wuLiWuTiShuZu[i]
			let jieGuo = wuLiWuTi.gengXinWuTi()
			jieGuoShuZu.push(jieGuo)
		}
		// 给主线程返回物体数据
		return jieGuoShuZu
	}
	/**
	 * 射线检测
	 * 起点在物体内部的物体不会被检测
	 * @param {Array<Number>} origin
	 * @param {Array<Number>} dest
	 */
	sheXianJianCe(origin, dest) {
		var tempVRayOrigin = new AmmoLib.btVector3();
		var tempVRayDest = new AmmoLib.btVector3();
		var closestRayResultCallback = new AmmoLib.ClosestRayResultCallback( tempVRayOrigin, tempVRayDest );
		var rayCallBack = AmmoLib.castObject( closestRayResultCallback, AmmoLib.RayResultCallback );
		rayCallBack.set_m_closestHitFraction( 1 );
		rayCallBack.set_m_collisionObject( null );
		tempVRayOrigin.setValue( ...origin );
		tempVRayDest.setValue( ...dest );
		closestRayResultCallback.get_m_rayFromWorld().setValue( ...origin );
		closestRayResultCallback.get_m_rayToWorld().setValue( ...dest );
		
		this.world.rayTest( tempVRayOrigin, tempVRayDest, closestRayResultCallback )
		
		if ( closestRayResultCallback.hasHit() ) {
			var point = closestRayResultCallback.get_m_hitPointWorld();
			var normal = closestRayResultCallback.get_m_hitNormalWorld();
			
			return point
		}
		
		return false
	}
	// 获取页面是否隐藏
	yeMianYinCang(canShu) {
		this.yinCang = canShu.yinCang
	}
	// 是否停止物理模拟
	tingZhiMoNi(canShu) {
		this.tingZhi = canShu.tingZhi
	}
	
	// 更新模型的操作
	gengXinCaoZuo(canShu) {
		for (let bianHao in canShu) {
			let wuTiCaoZuo = canShu[bianHao]
			wuTiCaoZuo.forEach(wtcz => this[wtcz.mingZi](wtcz.canShu))
		}
		
		return canShu
	}
	
	// 步进模拟 根据间隔时间更新物理世界状态
	buJinMoNi(jianGeShi) {
		if (this.tingZhi) return
		
		const shi = performance.now()
		jianGeShi ??= ( shi - this.shangShi ) / 1000
		this.shangShi = shi
		if (jianGeShi <= 0) return
		/*
		 高速物体需要更小的固定步长
		 jianGeShi < guDingBuChang 物理引擎会不模拟，此帧传过来的操作会全部无效
		*/
		let guDingBuChang = 1 / (this.zhenShu * 2)
		this.world.stepSimulation(jianGeShi, 10, guDingBuChang)

		this.pengZhuangJianCe()
	}
	// 碰撞检测
	pengZhuangJianCe() {
		const dispatcher = this.dispatcher
		
		for (let i = 0, il = WuLiWuTi.wuLiWuTiShuZu.length; i < il; i++) {
			const wuLiWuTi = WuLiWuTi.wuLiWuTiShuZu[i]
			if (wuLiWuTi.wuTiLeiXing == '角色') wuLiWuTi.zhanLiWuTi = null
		}
		// getNumManifolds不包含柔体
		xunHuan0: for (let i = 0, il = dispatcher.getNumManifolds(); i < il; i++) {
			const contactManifold = dispatcher.getManifoldByIndexInternal(i)
			const rb0 = AmmoLib.castObject(contactManifold.getBody0(), AmmoLib.btRigidBody)
			const rb1 = AmmoLib.castObject(contactManifold.getBody1(), AmmoLib.btRigidBody)

			let wuLiWuTi0, wuLiWuTi1
			xunHuan1: for (let i = 0, il = WuLiWuTi.wuLiWuTiShuZu.length; i < il; i++) {
				const wuLiWuTi = WuLiWuTi.wuLiWuTiShuZu[i]
				
				if (wuLiWuTi.wuTi === rb0) wuLiWuTi0 = wuLiWuTi
				if (wuLiWuTi.wuTi === rb1) wuLiWuTi1 = wuLiWuTi

				if (wuLiWuTi0 && wuLiWuTi1) break
			}

			if (!(wuLiWuTi0 && wuLiWuTi1)) continue xunHuan0

			for (let j = 0, jl = contactManifold.getNumContacts(); j < jl; j++) {
				const contactPoint = contactManifold.getContactPoint(j)

				if (contactPoint.getDistance().toFixed(2) <= 0) {
					// 角色接触检测
					// zhanLiWuTi有值不再检测，防止多点触碰被null覆盖
					if (wuLiWuTi0.wuTiLeiXing == '角色' && !wuLiWuTi0.zhanLiWuTi) wuLiWuTi0.jieChuJianCe('A', contactPoint, wuLiWuTi1)
					if (wuLiWuTi1.wuTiLeiXing == '角色' && !wuLiWuTi1.zhanLiWuTi) wuLiWuTi1.jieChuJianCe('B', contactPoint, wuLiWuTi0)
				}
			}
		}
	}
	
	xiaoHui() {
		clearInterval(this.intervalID)

		for (let i = 0, il = WuLiWuTi.wuLiWuTiShuZu.length; i < il; i++) {
			WuLiWuTi.wuLiWuTiShuZu[i].xiaoHuiWuTi()
		}
		
		// AmmoLib.destroy(this.world)
		// AmmoLib.destroy(this.softBodySolver)
		// AmmoLib.destroy(this.solver)
		// AmmoLib.destroy(this.broadphase)
		// AmmoLib.destroy(this.dispatcher)
		// AmmoLib.destroy(this.collisionConfiguration)
	}
}

let wuLiYinQing = null

globalThis.addEventListener('message', async (message) => {
	let { bianHao, mingZi, canShu } = message.data
	
	if (mingZi == '加载ammo') {
		await jiaZaiAmmo({ wasmDiZhi: canShu.wasmDiZhi })
		
		postMessage({ bianHao, mingZi: 'ammo加载完成' })
		
		wuLiYinQing = new WuLiYinQing(AmmoLib)
	} else {
		if (!wuLiYinQing) return
		// 根据mingZi调用wuLiYinQing同名方法
		let jieGuo = wuLiYinQing[mingZi](canShu)

		if (jieGuo) postMessage({ bianHao, mingZi, canShu: jieGuo })
	}
})